package arquiteturaTAK;

public class ALU {
	private boolean[] aSide;
	private boolean[] bSide;
	private boolean[] selector;
	
	private boolean flagCarry;
	private boolean flagNegative;
	private boolean flagZero;
	
	private final int A = 0;
	private final int ADD1A = 1;
	private final int B = 2;
	private final int ADD = 3;
	private final int SUBAB = 4;
	private final int SUBBA = 5;
	private final int CMP = 6;
	private final int AND = 7;
	private final int OR = 8;
	private final int NOTA = 9;
	private final int NOTB = 10;
	private final int CLR = 11;
	private final int NEGA = 12;
	private final int NEGB = 13;
	private final int SHLA = 14;
	private final int SHRA = 15;
	
	
	public ALU (int tamWord){
		this.aSide = new boolean[tamWord];
		this.bSide = new boolean[tamWord];
		this.selector = new boolean[4];
		
		flagCarry = false;
		
		for(int i=0; i<this.aSide.length; i++){
			this.aSide[i] = false;
			this.bSide[i] = false;
		}
		
		for(int i=0; i<this.selector.length; i++){
			this.selector[i] = false;
		}		
	}
	
	public boolean updateInputValue(boolean side, boolean[] value){
		if(value.length != this.aSide.length){
			return false;
		}
		
		if(side == false){
			this.aSide = value;
		} else{
			this.bSide = value;
		}
		return true;
	}
	
	public boolean updateSelectors(boolean[] value){
		if(value.length != selector.length){
			return false;
		}
		this.selector = value;
		return true;
	}
	
	public boolean[] getOutput(){
		int operation = ArithMath.booleanArrayToInt(selector);
		switch (operation){
			case A:
				return this.aSide;
			case ADD1A:
				return this.add(this.aSide, ArithMath.arrayBooleanOne(this.aSide.length));
			case B:
				return this.bSide;
			case ADD:
				return this.add(this.aSide, this.bSide);
			case SUBAB:
				return this.sub(this.aSide, this.bSide);
			case SUBBA:
				return this.sub(this.bSide, this.aSide);
			case CMP:
				return this.cmp(this.aSide, this.bSide);
			case AND:
				return this.and(this.aSide, this.bSide);
			case OR:
				return this.or(this.aSide, this.bSide);
			case NOTA:
				//Continuar daqui
				return this.or(this.aSide, this.bSide);
			case NOTB:
				return this.or(this.aSide, this.bSide);
			case CLR:
				return this.or(this.aSide, this.bSide);
			case NEGA:
				return this.or(this.aSide, this.bSide);
			case NEGB:
				return this.or(this.aSide, this.bSide);
			case SHLA:
				return this.or(this.aSide, this.bSide);
			case SHRA:
				return this.or(this.aSide, this.bSide);
			default:
				return this.aSide;
		}	
	}
	
	private boolean[] add(boolean[] value1, boolean[] value2){
		boolean carry = false;
		boolean[] result = new boolean[this.aSide.length];
		for(int i=0; i<result.length; i++){
			result[i] = ArithMath.xor(value1[i], value2[i]);
			result[i] = ArithMath.xor(result[i], carry);
			carry = ArithMath.and(value1[i], value2[i]);
		}
		this.flagCarry = carry;
		return result;
	}
	
	private boolean[] neg(boolean[] value){
		for(int i=0; i< value.length; i++){
			value[i] = !value[i];
		}
		value = this.add(value, ArithMath.arrayBooleanOne(this.aSide.length));
		return value;
	}
	
	private boolean[] sub(boolean[] value1, boolean[] value2){
		boolean[] result = new boolean[this.aSide.length];
		value2 = this.neg(value2);
		result = this.add(value1, value2);
		return result;
	}
	
	private boolean[] cmp(boolean[] value1, boolean[] value2){
		boolean[] result = new boolean[this.aSide.length];
		result = this.sub(value1, value2);
		if(result[0] == true){
			this.flagNegative = true;
		}
		for(int i=0; i<value1.length-1; i++){
			this.flagZero = !ArithMath.or(result[i], result[i+1]);			
		}
		return result;
	}
	
	private boolean[] and(boolean[] value1, boolean[] value2){
		boolean[] result = new boolean[this.aSide.length];
		for(int i=0; i<this.aSide.length; i++){
			result[i] = ArithMath.and(value1[i], value2[i]);
		}
		return result;
	}
	
	private boolean[] or(boolean[] value1, boolean[] value2){
		boolean[] result = new boolean[this.aSide.length];
		for(int i=0; i<this.aSide.length; i++){
			result[i] = ArithMath.or(value1[i], value2[i]);
		}
		return result;
	}
}
